package ltg.ibutton.hydroroom.gui.tablet;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Observable;
import java.util.Observer;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JPanel;

import ltg.ibutton.core.Concept;
import ltg.ibutton.hydroroom.DyeTracer;
import ltg.ibutton.hydroroom.HydroRoomPersistence;
import ltg.ibutton.hydroroom.InjectedDyeTracer;
import ltg.ibutton.hydroroom.WaterSample;

public class LeftPanel extends JPanel implements Observer {
	private static final long serialVersionUID = 1L;
	private static final int BORDER_LEFT_MIN = 60;
	private static final int BORDER_RIGHT = 10;
	private static final int OFFSET_Y = 100;
	
	private static final int NO_DIR = 1;
	private static final int N = 2;
	private static final int NE = 3;
	private static final int E = 4;
	private static final int SE = 5;
	private static final int S = 6;
	private static final int SW = 7;
	private static final int W = 8;
	private static final int NW = 9;
	
	private Hashtable<Point, Integer> coloredCells = null;
	private Color cellColor = null;
	private Rectangle currentTile = null;
	
	private float w, h, blockW, blockH, pxOnM;
	private float roomwidth, roomheight, xblocks, yblocks;
	private Hashtable<String, BufferedImage> imagemap = null;
	private ArrayList<ScreenConcept> attachedButtons = null;
	private int iconindex = -1;
	
	private JButton confirmButton = null;
	private Concept c = null;
	
	private int actualBorderLeft = BORDER_LEFT_MIN;
	
	public LeftPanel(JButton confirmButton) {
		attachedButtons = new ArrayList<ScreenConcept>();
		coloredCells = HydroRoomPersistence.getColoredTiles();
		String[] stationcolor = HydroRoomPersistence.getTilesColor();
		cellColor = new Color(Integer.parseInt(stationcolor[0]), 
				Integer.parseInt(stationcolor[1]), 
				Integer.parseInt(stationcolor[2]));
		
		this.confirmButton = confirmButton;
		
        addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
	            	iconindex = -1;
	            	for(int j=0; j < attachedButtons.size(); j++) {
	        			if(attachedButtons.get(j).contains(e.getX(),e.getY()) && !attachedButtons.get(j).isStored()) {
	        				iconindex = j;
	        				break;
	        			}
	        		}            	
            }
            
            public void mouseClicked(MouseEvent e) {
            	if(inCanvas(e.getX(), e.getY()))
        			tagUnTagOperation(e);
            }
        });

        addMouseMotionListener(new MouseAdapter() {
            public void mouseDragged(MouseEvent e) {
            	if(inCanvas(e.getX(), e.getY())) { //highligthing the current tile
            		int x = (int) Math.floor((e.getX()-actualBorderLeft)/blockW);
            		int y = (int) Math.floor((e.getY()-OFFSET_Y)/blockH);
            		currentTile = new Rectangle((int) Math.ceil(x*blockW+actualBorderLeft),
            				(int) Math.ceil(y*blockH+OFFSET_Y),
            				(int) Math.ceil(blockW),
            				(int) Math.ceil(blockH));
            	}
            	
        		if(iconindex > -1) { //selecting an icon
        		   moveIcon(iconindex, e.getX(),e.getY());
        		   repaint();
        		}
        	}
            
            public void mouseMoved(MouseEvent e) {
            	Rectangle tmpRect = null;
            	if(inCanvas(e.getX(), e.getY())) {
            		int x = (int) Math.floor((e.getX()-actualBorderLeft)/blockW);
            		int y = (int) Math.floor((e.getY()-OFFSET_Y)/blockH);
            		tmpRect = new Rectangle((int) Math.ceil(x*blockW+actualBorderLeft),
            				(int) Math.ceil(y*blockH+OFFSET_Y),
            				(int) Math.ceil(blockW),
            				(int) Math.ceil(blockH));
            	}
            	else
            		tmpRect = null;
            	
            	if((tmpRect != null && currentTile == null) ||
            			(tmpRect == null && currentTile != null) ||
            			(tmpRect != null && !tmpRect.equals(currentTile))) {
            		currentTile = tmpRect;
            		repaint();
            		//System.err.println("repainting...");
            	}
            }
        });
        
        imagemap = new Hashtable<String, BufferedImage>();
        try {
			imagemap.put("bluepin", ImageIO.read(new File("img/bluepin.gif")));
			imagemap.put("purplepin", ImageIO.read(new File("img/purplepin.gif")));
			imagemap.put("violet", ImageIO.read(new File("img/violetdye.png")));
			imagemap.put("blue", ImageIO.read(new File("img/bluedye.png")));
			imagemap.put("green", ImageIO.read(new File("img/greendye.png")));
			imagemap.put("yellow", ImageIO.read(new File("img/yellowdye.png")));
			imagemap.put("orange", ImageIO.read(new File("img/orangedye.png")));
	        imagemap.put("red", ImageIO.read(new File("img/reddye.png")));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		
		Hashtable<String, Float> classroomconf = HydroRoomPersistence.getClassroomConf();
		roomwidth = classroomconf.get("width");
		roomheight = classroomconf.get("height");
		xblocks = classroomconf.get("xblocks").intValue();
		yblocks = classroomconf.get("yblocks").intValue();
				
		this.setOpaque(true);
    }
	
	public void tagUnTagOperation(MouseEvent e) {
		int x = (int) Math.floor((e.getX()-actualBorderLeft)/blockW);
		int y = (int) Math.floor((e.getY()-OFFSET_Y)/blockH);
		//System.err.println(x + " " + y);
		Point pt = new Point(x,y);
		if(coloredCells.get(pt) == null || coloredCells.get(pt) != NW) {
			if(coloredCells.get(pt) == null)
				coloredCells.put(pt, NO_DIR);
			else {
				int dir = coloredCells.get(pt);
				coloredCells.put(pt, new Integer(++dir%10));
			}
		}
		else
			coloredCells.remove(pt);
		
		HydroRoomPersistence.setColoredTiles(coloredCells);
		repaint();
	}
	
	private boolean inCanvas(int x, int y) {
		Rectangle canvas = new Rectangle(actualBorderLeft, OFFSET_Y, (int) Math.ceil(w), (int) Math.ceil(h));
		return canvas.contains(x, y);
	}
	
	private void initCanvas() {
		w = this.getWidth()-BORDER_LEFT_MIN-BORDER_RIGHT;
		h = (w*roomheight/roomwidth);
		if((this.getHeight() - OFFSET_Y - 30) < h) {
			h = this.getHeight() - OFFSET_Y - 30;
			w = (h*roomwidth/roomheight);
		}
		blockW = w / xblocks;
		blockH = h / yblocks;
		pxOnM = w / roomwidth;
		actualBorderLeft = (int) Math.round(this.getWidth()-w)/2;
	}
	
	private void drawGrid(Graphics2D g2d) {
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        g2d.setColor(Color.BLACK); //columns & letter grid
        g2d.drawRect(actualBorderLeft, OFFSET_Y, (int) Math.ceil(w), (int) Math.ceil(h));
        for(int i = 0; i < xblocks; i++) {
        	int charsize = g2d.getFontMetrics().charWidth((char) (i + (int) 'A'));
        	g2d.drawString(String.valueOf((char) (i + (int) 'A')), actualBorderLeft + ((blockW-charsize)/2) + i * blockW, OFFSET_Y-10);
        	g2d.drawLine((int) Math.ceil(actualBorderLeft + i * blockW), OFFSET_Y, (int) Math.ceil(actualBorderLeft + i * blockW), (int) Math.ceil(OFFSET_Y + h));
        }
        
        int charheight = g2d.getFontMetrics().getAscent();
        for(int i = 0; i < yblocks; i++) { //rows & number grid
        	int charsize = g2d.getFontMetrics().stringWidth(String.valueOf(i+1));
        	g2d.drawString(String.valueOf(i+1), actualBorderLeft-20-charsize/2, OFFSET_Y + blockH*(i+1) - (blockH - charheight)/2 );
        	g2d.drawLine(actualBorderLeft, (int) Math.ceil(OFFSET_Y + i * blockH), (int) Math.ceil(actualBorderLeft + w), (int) Math.ceil(OFFSET_Y + i * blockH));
        }
	}
	
	private void drawIcons(Graphics2D g2d) {
		ResultSet rs = HydroRoomPersistence.getActionsByStation();
        try {
			while(rs.next()) {
				int x = Math.round(rs.getFloat("x")*pxOnM);
				int y = Math.round(rs.getFloat("y")*pxOnM);
				String info = rs.getString("info");
				String type = rs.getString("type");
				if(type.equals("D")) {
					String color = info.substring(info.indexOf("=")+1);
					BufferedImage icon = imagemap.get(color);
					g2d.drawImage(icon, 
							(int) Math.ceil(actualBorderLeft+x-icon.getWidth()/2), 
							(int) Math.ceil(OFFSET_Y+y-icon.getHeight()), 
							null);
				}
				else {
					BufferedImage icon = imagemap.get("bluepin");
					g2d.drawImage(icon,
							(int) Math.ceil(actualBorderLeft+x-icon.getWidth()/2),
							(int) Math.ceil(OFFSET_Y+y-icon.getHeight()),
							null);
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
        HydroRoomPersistence.closeEverything();
	}
	
	private void drawArrow(Graphics2D g2d, Point p, int dir) {
		g2d.setColor(Color.black);
		int startX = (int) (p.getX()*blockW+actualBorderLeft);
		int startY = (int) (p.getY()*blockH+OFFSET_Y);
		switch(dir) {
			case N:
			case S:
				g2d.drawLine((int) (startX+blockW/2), startY+5, (int) (startX+blockW/2), (int) (startY+blockH-5));
				break;
			case E:
			case W:
				g2d.drawLine(startX+5, (int) (startY+blockH/2), (int) (startX+blockW-5), (int) (startY+blockH/2));
				break;
			case NE:
			case SW:
				g2d.drawLine(startX+5, (int) (startY+blockH-5), (int) (startX+blockW-5), startY+5);
				break;
			case SE:
			case NW:
				g2d.drawLine(startX+5, startY+5, (int) (startX+blockW-5), (int) (startY+blockH-5));
				break;
			default:
		}
		int xPoints[] = new int[3];
		int yPoints[] = new int[3];
		double alpha = Math.asin(blockH/Math.sqrt(Math.pow(blockW, 2)+Math.pow(blockH, 2)));
		switch(dir) {
			case N:
				xPoints[0] = (int) Math.ceil(startX+blockW/2);
				yPoints[0] = startY+5;
				
				xPoints[1] = (int) Math.ceil(startX+blockW/2-5);
				yPoints[1] = startY+15;
				
				xPoints[2] = (int) Math.ceil(startX+blockW/2+5);
				yPoints[2] = startY+15;
				break;
			case NE:
				xPoints[0] = (int) (startX+blockW-5);
				yPoints[0] = startY+5;
				
				xPoints[1] = (int) Math.ceil(startX+blockW-5-5*(2*Math.cos(alpha)+Math.sin(alpha)));
				yPoints[1] = (int) Math.ceil(startY+5+5*(2*Math.sin(alpha)-Math.cos(alpha)));
				
				xPoints[2] = (int) Math.ceil(startX+blockW-5-5*(2*Math.cos(alpha)-Math.sin(alpha)));
				yPoints[2] = (int) Math.ceil(startY+5+5*(2*Math.sin(alpha)+Math.cos(alpha)));
				break;
			case E:
				xPoints[0] = (int) Math.ceil(startX+blockW-5);
				yPoints[0] = (int) Math.ceil(startY+blockH/2);
				
				xPoints[1] = (int) Math.ceil(startX+blockW-15);
				yPoints[1] = (int) Math.ceil(startY+blockH/2-5);
				
				xPoints[2] = (int) Math.ceil(startX+blockW-15);
				yPoints[2] = (int) Math.ceil(startY+blockH/2+5);
				break;
			case SE:
				xPoints[0] = (int) (startX+blockW-5);
				yPoints[0] = (int) Math.ceil(startY+blockH-5);
				
				xPoints[1] = (int) Math.ceil(startX+blockW-5-5*(2*Math.cos(alpha)+Math.sin(alpha)));
				yPoints[1] = (int) Math.ceil(startY+blockH-5+5*(2*Math.sin(alpha)-Math.cos(alpha)));
				
				xPoints[2] = (int) Math.ceil(startX+blockW-5-5*(2*Math.cos(alpha)-Math.sin(alpha)));
				yPoints[2] = (int) Math.ceil(startY+blockH-5-5*(2*Math.sin(alpha)+Math.cos(alpha)));
				break;
			case S:
				xPoints[0] = (int) Math.ceil(startX+blockW/2);
				yPoints[0] = (int) Math.ceil(startY+blockH-5);
				
				xPoints[1] = (int) Math.ceil(startX+blockW/2-5);
				yPoints[1] = (int) Math.ceil(startY+blockH-15);
				
				xPoints[2] = (int) Math.ceil(startX+blockW/2+5);
				yPoints[2] = (int) Math.ceil(startY+blockH-15);
				break;
			case SW:
				xPoints[0] = startX+5;
				yPoints[0] = (int) Math.ceil(startY+blockH-5);
				
				xPoints[1] = (int) Math.ceil(startX+5+5*(2*Math.cos(alpha)-Math.sin(alpha)));
				yPoints[1] = (int) Math.ceil(startY+blockH-5-5*(2*Math.sin(alpha)+Math.cos(alpha)));
				
				xPoints[2] = (int) Math.ceil(startX+5+5*(2*Math.cos(alpha)+Math.sin(alpha)));
				yPoints[2] = (int) Math.ceil(startY+blockH-5-5*(2*Math.sin(alpha)-Math.cos(alpha)));
				break;
			case W:
				xPoints[0] = startX+5;
				yPoints[0] = (int) Math.ceil(startY+blockH/2);
				
				xPoints[1] = startX+15;
				yPoints[1] = (int) Math.ceil(startY+blockH/2-5);
				
				xPoints[2] = startX+15;
				yPoints[2] = (int) Math.ceil(startY+blockH/2+5);
				break;
			case NW:
				xPoints[0] = startX+5;
				yPoints[0] = startY+5;
				
				xPoints[1] = (int) Math.ceil(startX+5+5*(2*Math.cos(alpha)+Math.sin(alpha)));
				yPoints[1] = (int) Math.ceil(startY+5+5*(2*Math.sin(alpha)-Math.cos(alpha)));
				
				xPoints[2] = (int) Math.ceil(startX+5+5*(2*Math.cos(alpha)-Math.sin(alpha)));
				yPoints[2] = (int) Math.ceil(startY+5+5*(2*Math.sin(alpha)+Math.cos(alpha)));
				break;
			default:
		}
		g2d.fillPolygon(xPoints, yPoints, 3);
	}	
	
	private void moveIcon(int i, int x, int y) {
        ScreenConcept tmp = attachedButtons.get(i);
        final int CURR_X = (int) tmp.getX();
        final int CURR_Y = (int) tmp.getY();
        final int CURR_W = (int) tmp.getWidth();
        final int CURR_H = (int) tmp.getHeight();
        final int OFFSET = 1;

        if ((CURR_X!=x) || (CURR_Y!=y)) {
            // The square is moving, repaint background over the old square location. 
            repaint(CURR_X,CURR_Y,CURR_W+OFFSET,CURR_H+OFFSET);
            // Update coordinates.
            tmp.setLocation(x-CURR_W/2, y-CURR_H);
            
            if(inCanvas((int) Math.ceil(tmp.getX()+tmp.getWidth()/2), (int) Math.ceil(tmp.getY()+tmp.getHeight())))
            	confirmButton.setEnabled(true);
            else
            	confirmButton.setEnabled(false);
            
            // Repaint the square at the new location.
            repaint((int) tmp.getX(), (int) tmp.getY(), 
            		(int) tmp.getWidth()+OFFSET, 
            		(int) tmp.getHeight()+OFFSET);
        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        initCanvas();        
        
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        g2d.setColor(Color.WHITE);
        g2d.fillRect(actualBorderLeft, OFFSET_Y, (int) Math.ceil(w), (int) Math.ceil(h)); //drawing the canvas

        if(currentTile != null) {
        	g2d.setColor(new Color(255,255,150));
        	g2d.fill(currentTile);
        }
        
        for(Enumeration<Point> en = coloredCells.keys(); en.hasMoreElements();) { //drawing the colored blocks
        	Point tmp = en.nextElement();
        	g2d.setColor(cellColor);
        	g2d.fillRect((int) Math.ceil(tmp.getX()*blockW+actualBorderLeft), (int) Math.ceil(tmp.getY()*blockH+OFFSET_Y), (int) Math.ceil(blockW), (int) Math.ceil(blockH));
        	this.drawArrow(g2d, tmp, coloredCells.get(tmp));
        }
        
        this.drawGrid(g2d); //drawing the grid
        this.drawIcons(g2d); //drawing the pins
        
        for(Iterator<ScreenConcept> j = attachedButtons.iterator(); j.hasNext();) {
			j.next().paintIcon(g2d); //drawing attached buttons icons
		}
    }

	public void update(Observable arg0, Object arg1) {
		ArrayList<String> ib = (ArrayList<String>) arg1; //list of current attached buttons
		if(ib.isEmpty())
			confirmButton.setEnabled(false);
		
		if(ib.isEmpty() || ib.size() != attachedButtons.size()) {
			attachedButtons.clear();
		}
		
		int xoffset = 0;
		for(Iterator<String> j = ib.iterator(); j.hasNext();) {
			String bid = (String) j.next();
			c = HydroRoomPersistence.getConceptFromBid(bid);
			if(c.getClass().getName().equals("ltg.ibutton.hydroroom.DyeTracer")) {
				attachedButtons.add(new ScreenConcept(actualBorderLeft+xoffset, 10, ((DyeTracer) c).getColor(), false));
			}
			else {
				if((((WaterSample) c).getX() == -1) && (((WaterSample) c).getY() == -1)) { //new water sample
					attachedButtons.add(new ScreenConcept(actualBorderLeft+xoffset, 10, null, false));
				}
				else {
					int x = Math.round(((WaterSample) c).getX()*pxOnM);
					int y = Math.round(((WaterSample) c).getY()*pxOnM);
					BufferedImage icon = imagemap.get("purplepin");
					attachedButtons.add(new ScreenConcept( //already stored water sample
							(int) Math.ceil(actualBorderLeft+x-icon.getWidth()/2), 
							(int) Math.ceil(OFFSET_Y+y-icon.getHeight()), 
							null, true));
				}
			}
			xoffset += 50;
		}
		this.repaint();
	}
	
	public void confirmAction(TabletGui gui) {
		ScreenConcept tmp = attachedButtons.get(0);
	
		//remember that icon coords are referred to the top-left corner
		float x = (float) ((tmp.getX()+tmp.getWidth()/2-actualBorderLeft)/pxOnM);
		float y = (float) ((tmp.getY()+tmp.getHeight()-OFFSET_Y)/pxOnM);
		
		Concept newc = null;
        if(c.getClass().getName().equals("ltg.ibutton.hydroroom.WaterSample")) {
    		newc = (WaterSample) c;
    		((WaterSample) newc).setX(x);
    		((WaterSample) newc).setY(y);
    		System.err.println("inserting a water sample");
    	}
    	else {
    		newc = new InjectedDyeTracer((DyeTracer) c);
    		((InjectedDyeTracer) newc).setX(x);
    		((InjectedDyeTracer) newc).setY(y);
    		((InjectedDyeTracer) newc).setStationId(HydroRoomPersistence.getThisStationId());
    		System.err.println("inserting a dye");
    	}
           
        confirmButton.setEnabled(false);
        System.err.println("STORING INTO DB");
		HydroRoomPersistence.storeConcept(newc);
		gui.updateTable();
		
		//task = new MyProgressBar(x, y, gui);
	}
	
	private class ScreenConcept extends Rectangle {
		private static final long serialVersionUID = 1L;
		private String color = null;
		private boolean stored = false;

	    public ScreenConcept(int x, int y, String color, boolean stored) {
	    	super(x,y,0,0);
	    	
	    	if(color != null)
	    		this.color = color;
	    	this.stored = stored;
	    }
	    
	    public boolean isStored() {
	    	return this.stored;
	    }
	    
	    public void paintIcon(Graphics2D g2d){
	    	BufferedImage icon = null;
	    	String scaffolding = "";
	    	String scaffolding1 = "";
	    	String scaffolding2 = "";
	    	if(this.color != null) {
	    		icon = imagemap.get(this.color);
	    		scaffolding = "A tube containing a "+this.color+" dye tracer has been detected.";
	    		scaffolding1 = "Drag the icon onto your location in the map";
	    		scaffolding2 = "Click on \"Confirm\" to inject it.";
	    	}
			else {
				if(this.stored) {
					icon = imagemap.get("purplepin");
					scaffolding = "A filled tube has been detected.";
					scaffolding1 = "The purple pin indicates where that sample was take.";
					scaffolding2 = "";
				}
				else {
					icon = imagemap.get("bluepin");
					scaffolding = "An empty tube has been detected.";
					scaffolding1 = "Drag the icon onto your location in the map";
					scaffolding2 = "and click on \"Confirm\" to take a water sample.";
				}
			}
	    	
	    	this.setSize(icon.getWidth(), icon.getHeight());
	    	g2d.drawImage(icon, this.x, this.y, null);
	    	g2d.setColor(Color.black);
	    	g2d.drawString(scaffolding, actualBorderLeft + 150, 20);
	    	g2d.drawString(scaffolding1, actualBorderLeft + 150, 35);
	    	g2d.drawString(scaffolding2, actualBorderLeft + 150, 50);
	    }
	}
}